package cn.ivicar.mapper.service.impl;

import java.util.Date;
import java.util.List;

import cn.ivicar.mapper.ibatis.mapper.BaseMapper;
import cn.ivicar.mapper.ibatis.model.BaseExample;
import cn.ivicar.mapper.model.BaseDataObject;
import cn.ivicar.mapper.replicable.ReplicableSqlSession;
import cn.ivicar.mapper.replicable.ReplicableSqlSessionFactory;
import cn.ivicar.mapper.service.CRUDManager;
import com.google.inject.Inject;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SuppressWarnings("unchecked")
public class GenericBaseManager<T extends BaseDataObject, K> extends BaseManager implements CRUDManager<T, K> {
    private static final Logger logger = LoggerFactory.getLogger(GenericBaseManager.class);

    private BaseMapper<T, BaseExample, K> mapper;

    @SuppressWarnings("rawtypes")
    private Class<? extends BaseMapper> mappType;

    private Class<T> type;

    @Inject
    protected ReplicableSqlSessionFactory sqlSessionFactory;

    @SuppressWarnings("rawtypes")
    public GenericBaseManager(BaseMapper<T, BaseExample, K> mapper, Class<T> type,
            Class<? extends BaseMapper> mappType) {
        if (mapper == null) {
            throw new IllegalArgumentException("mapper不能为空。");
        }

        this.mapper = mapper;
        this.type = type;
        this.mappType = mappType;
    }

    public int countByExample(BaseExample example) {
        return countByExample(example, false);
    }

    public int countByExample(BaseExample example, boolean replicable) {
        if (replicable) {
            return replicableCountByExample(example, replicable);
        } else {
            return this.mapper.countByExample(example);
        }
    }

    public int delete(T object) {
        try {
            BaseExample baseExample = this.getExample();
            baseExample.or().andObjectByExample(object);

            return this.mapper.deleteByExample(baseExample);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public int deleteByBaseExample(BaseExample example) {
        try {
            return this.mapper.deleteByExample(example);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public int deleteByPrimaryKey(K id) {
        try {
            return this.mapper.deleteByPrimaryKey(id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public T get(K parameter) {
        return get(parameter, false);
    }

    public T get(K parameter, boolean replicable) {
        if (replicable) {
            return replicableGet(parameter, replicable);
        } else {
            return this.mapper.selectByPrimaryKey(parameter);
        }
    }

    public BaseExample getExample() {
        Configuration conf = this.getSqlSessionFactory().getConfiguration();
        String resultMapName = "ResultMap" + this.type.getSimpleName();
        ResultMap resultMap = conf.getResultMap(resultMapName);
        BaseExample baseExample = new BaseExample(resultMap);

        return baseExample;
    }

    public ReplicableSqlSessionFactory getSqlSessionFactory() {
        return this.sqlSessionFactory;
    }

    public int insert(T object) {
        object.setCreated(new Date());
        object.setDeleted(null);

        return this.mapper.insert(object);
    }

    public List<T> list() {
        return list(false);
    }

    public List<T> list(boolean replicable) {
        if (replicable) {
            return replicableList(replicable);
        } else {
            BaseExample baseExample = this.getExample();

            return this.mapper.selectByExample(baseExample);
        }
    }

    public List<T> listByBaseExample(BaseExample baseExample) {
        return listByBaseExample(baseExample, false);
    }

    public List<T> listByBaseExample(BaseExample baseExample, boolean replicable) {
        if (replicable) {
            return replicableListByBaseExample(baseExample, replicable);
        } else {
            return this.mapper.selectByExample(baseExample);
        }
    }

    public List<T> listByBaseExampleWithBLOBs(BaseExample baseExample) {
        return listByBaseExampleWithBLOBs(baseExample, false);
    }

    public List<T> listByBaseExampleWithBLOBs(BaseExample baseExample, boolean replicable) {
        if (replicable) {
            return replicableListByBaseExampleWithBLOBs(baseExample, replicable);
        } else {
            return this.mapper.selectByExampleWithBLOBs(baseExample);
        }
    }

    public List<T> listByExample(T example) {
        return listByExample(example, false);
    }

    public List<T> listByExample(T example, boolean replicable) {
        if (replicable) {
            return replicableListByExample(example, replicable);
        } else {
            try {
                BaseExample baseExample = this.getExample();
                baseExample.or().andObjectByExample(example);

                return this.mapper.selectByExample(baseExample);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public int logicalDelete(T object) {
        try {
            object.setDeleted(new Date());
            return this.mapper.updateByPrimaryKey(object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public int logicalRecover(T object) {
        try {
            object.setDeleted(null);
            return this.mapper.updateByPrimaryKey(object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void setSqlSessionFactory(ReplicableSqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    public int update(T object) {
        object.setUpdated(new Date());
        return this.mapper.updateByPrimaryKey(object);
    }

    public int updateByBaseExample(T record, BaseExample example) {
        try {
            return this.mapper.updateByExample(record, example);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public int updateByBaseExampleSelective(T record, BaseExample example) {
        try {
            return this.mapper.updateByExampleSelective(record, example);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public int updateSelective(T object) {
        object.setUpdated(new Date());
        return this.mapper.updateByPrimaryKeySelective(object);
    }

    protected int replicableCountByExample(BaseExample example, boolean replicable) {
        ReplicableSqlSession session = this.sqlSessionFactory.openReplicableSqlSession(replicable);
        try {
            return session.getMapper(this.mappType).countByExample(example);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            session.close();
        }
    }

    protected T replicableGet(K parameter, boolean replicable) {
        ReplicableSqlSession session = this.sqlSessionFactory.openReplicableSqlSession(replicable);
        try {
            BaseMapper<T, BaseExample, K> mapper = session.getMapper(this.mappType);
            return mapper.selectByPrimaryKey(parameter);
        } finally {
            session.close();
        }
    }

    protected List<T> replicableList(boolean replicable) {
        ReplicableSqlSession session = this.sqlSessionFactory.openReplicableSqlSession(replicable);
        try {
            BaseExample baseExample = this.getExample();

            return session.getMapper(this.mappType).selectByExample(baseExample);
        } finally {
            session.close();
        }
    }

    protected List<T> replicableListByBaseExample(BaseExample baseExample, boolean replicable) {
        ReplicableSqlSession session = this.sqlSessionFactory.openReplicableSqlSession(replicable);
        try {
            return session.getMapper(this.mappType).selectByExample(baseExample);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            session.close();
        }
    }

    protected List<T> replicableListByBaseExampleWithBLOBs(BaseExample baseExample,
            boolean replicable) {
        ReplicableSqlSession session = this.sqlSessionFactory.openReplicableSqlSession(replicable);
        try {
            return session.getMapper(this.mappType).selectByExampleWithBLOBs(baseExample);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            session.close();
        }
    }

    protected List<T> replicableListByExample(T example, boolean replicable) {
        ReplicableSqlSession session = this.sqlSessionFactory.openReplicableSqlSession(replicable);
        try {
            BaseExample baseExample = this.getExample();
            baseExample.or().andObjectByExample(example);

            return session.getMapper(this.mappType).selectByExample(baseExample);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            session.close();
        }
    }
}
